<!-- 镜像更新状态组件 -->
<template>
  <div class="image-update-status">
    <!-- 主要进度显示区域 -->
    <div class="main-progress-section">
      <div class="progress-header">
        <h2>📊 镜像检测进度</h2>
        <div class="overall-status" :class="overallStatusClass">
          {{ overallStatusText }}
        </div>
      </div>
      
      <!-- 环形进度条 -->
      <div class="circular-progress">
        <div class="progress-circle" :class="{ 'spinning': isOperating }">
          <svg width="120" height="120" viewBox="0 0 120 120">
            <circle cx="60" cy="60" r="50" stroke="#e5e7eb" stroke-width="8" fill="none"/>
            <circle cx="60" cy="60" r="50" stroke="#3b82f6" stroke-width="8" fill="none"
                    :stroke-dasharray="314" 
                    :stroke-dashoffset="314 - (314 * status.progress / 100)"
                    stroke-linecap="round" 
                    transform="rotate(-90 60 60)"
                    class="progress-fill"/>
          </svg>
          <div class="progress-text">
            <div class="progress-number">{{ Math.round(status.progress) }}%</div>
            <div class="progress-status">{{ currentStepText }}</div>
          </div>
        </div>
      </div>
      
      <!-- 详细状态消息 -->
      <div class="status-message" v-if="status.message">
        {{ status.message }}
      </div>
    </div>

    <!-- 步骤详情列表 -->
    <div class="steps-section">
      <h3>🔍 检测步骤</h3>
      <div class="steps-list">
        <div 
          v-for="(step, index) in steps" 
          :key="step.id"
          class="step-item"
          :class="getStepClass(step.status)"
        >
          <div class="step-icon">
            <i :class="getStepIcon(step.status)"></i>
          </div>
          <div class="step-content">
            <div class="step-title">
              {{ index + 1 }}. {{ step.name }}
            </div>
            <div class="step-detail" v-if="step.detail">
              {{ step.detail }}
            </div>
          </div>
          <div class="step-actions">
          <div class="step-status">
            <span class="status-badge" :class="getStatusBadgeClass(step.status)">
              {{ getStatusText(step.status) }}
            </span>
            </div>
            <div class="step-buttons">
              <!-- 执行按钮 - 所有步骤都显示 -->
              <button 
                class="step-button execute-btn"
                @click="executeStep(step.id, index)"
                :disabled="isStepExecuting(step.id) || (step.status === 'ok' && !canRetryStep(step))"
                :class="{ 'executing': isStepExecuting(step.id) }"
              >
                <i class="fas fa-play" :class="{ 'fa-spin fa-spinner': isStepExecuting(step.id) }"></i>
                {{ isStepExecuting(step.id) ? '执行中...' : (step.status === 'ok' ? '重新执行' : '执行') }}
              </button>
              
              <!-- 重试按钮 - 失败时显示 -->
              <button 
                v-if="step.status === 'fail'"
                class="step-button retry-btn"
                @click="retryStep(step.id, index)"
                :disabled="isStepExecuting(step.id)"
              >
                <i class="fas fa-redo"></i>
                重试
              </button>
              
              <!-- 跳过按钮 - 待执行或失败时显示 -->
              <button 
                v-if="step.status === 'pending' || step.status === 'fail'"
                class="step-button skip-btn"
                @click="skipStep(step.id, index)"
                :disabled="isStepExecuting(step.id)"
              >
                <i class="fas fa-forward"></i>
                跳过
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- 操作按钮 -->
    <div class="action-section">
      <div class="mode-controls">
      <button 
          class="mode-toggle-btn"
          @click="toggleManualMode"
          :class="{ 'manual-active': manualMode }"
        >
          <i :class="manualMode ? 'fas fa-hand-paper' : 'fas fa-play-circle'"></i>
          {{ manualMode ? '手动模式' : '自动模式' }}
        </button>
      </div>
      
      <button 
        v-if="!manualMode"
        class="check-button" 
        @click="checkUpdates"
        :disabled="isOperating"
        :class="{ 'checking': isOperating }"
      >
        <i class="fas fa-sync" :class="{ 'fa-spin': isOperating }"></i>
        {{ isOperating ? '检测中...' : '开始完整检测' }}
      </button>
      
      <div v-if="manualMode" class="manual-mode-info">
        <i class="fas fa-info-circle"></i>
        <span>手动模式：点击每个步骤的"执行"按钮来单独运行各个步骤</span>
      </div>
    </div>

    <!-- 执行日志和中间结果显示区域 -->
    <div v-if="showLogs" class="execution-logs">
      <el-card shadow="never" class="logs-card">
        <template #header>
          <div class="logs-header">
            <h4><i class="fas fa-terminal"></i> 执行日志</h4>
            <div class="logs-controls">
              <el-button @click="clearLogs" size="small" type="warning" plain>
                <i class="fas fa-trash"></i> 清空日志
              </el-button>
              <el-button @click="toggleLogsExpand" size="small" plain>
                <i :class="logsExpanded ? 'fas fa-compress' : 'fas fa-expand'"></i>
                {{ logsExpanded ? '收起' : '展开' }}
              </el-button>
            </div>
          </div>
        </template>
        
        <div class="logs-content" :class="{ 'expanded': logsExpanded }">
          <!-- 执行日志 -->
          <div v-if="executionLogs.length > 0" class="log-section">
            <h5><i class="fas fa-list-ul"></i> 实时日志</h5>
            <div class="log-entries">
              <div 
                v-for="(log, index) in executionLogs" 
                :key="index" 
                class="log-entry"
                :class="'log-' + log.type"
              >
                <div class="log-time">{{ formatTime(log.timestamp) }}</div>
                <div class="log-message">
                  <i :class="getLogIcon(log.type)"></i>
                  {{ log.message }}
                </div>
                <div v-if="log.details && Object.keys(log.details).length > 0" class="log-details">
                  <el-collapse accordion>
                    <el-collapse-item title="查看详情" :name="index">
                      <pre>{{ JSON.stringify(log.details, null, 2) }}</pre>
                    </el-collapse-item>
                  </el-collapse>
                </div>
              </div>
            </div>
          </div>

          <!-- 中间结果 -->
          <div v-if="Object.keys(intermediateResults).length > 0" class="result-section">
            <h5><i class="fas fa-chart-bar"></i> 中间结果</h5>
            <div class="result-entries">
              <div 
                v-for="(result, step) in intermediateResults" 
                :key="step" 
                class="result-entry"
              >
                <div class="result-header">
                  <span class="result-step">{{ getStepDisplayName(step) }}</span>
                  <span class="result-time">{{ formatTime(result.timestamp) }}</span>
                </div>
                <div class="result-content">
                  <el-descriptions :column="2" size="small" border>
                    <el-descriptions-item 
                      v-for="(value, key) in result.data" 
                      :key="key"
                      :label="key"
                    >
                      <span v-if="typeof value === 'object'">{{ JSON.stringify(value) }}</span>
                      <span v-else>{{ value }}</span>
                    </el-descriptions-item>
                  </el-descriptions>
                </div>
              </div>
            </div>
          </div>

          <div v-if="executionLogs.length === 0 && Object.keys(intermediateResults).length === 0" class="no-logs">
            <i class="fas fa-info-circle"></i>
            执行更新检查后将显示详细日志...
          </div>
        </div>
      </el-card>
    </div>


    <!-- 结果显示 -->
    <div v-if="hasUpdates" class="updates-section">
      <h3>🎯 发现更新</h3>
      <div class="update-list">
        <div 
          v-for="update in status.available_updates" 
          :key="update.name"
          class="update-item"
        >
          <div class="update-info">
            <strong>{{ update.name }}</strong>
            <span class="update-reason">{{ update.reason }}</span>
          </div>
          <div class="update-image">{{ update.image }}</div>
        </div>
      </div>
    </div>

    <!-- 错误显示 -->
    <div v-if="status.error" class="error-section">
      <i class="fas fa-exclamation-triangle"></i>
      <span>{{ status.error }}</span>
    </div>

    <!-- 最后检查时间 -->
    <div v-if="status.last_check" class="last-check">
      最后检查: {{ formatTime(status.last_check) }}
    </div>
  </div>
</template>

<script>
import { io } from 'socket.io-client';
import api from '@/services/api';

export default {
  name: 'ImageUpdateStatus',
  data() {
    return {
      status: {
        checking: false,
        updating: false,
        last_check: null,
        last_update: null,
        available_updates: [],
        current_operation: null,
        progress: 0,
        message: '就绪',
        error: null
      },
      // MoBox智能自动更新系统的5步骤检测流程
      steps: [
        { id: 'lwx_parse', name: 'LWX平台数据解析', status: 'pending', detail: '解析LWX平台返回的Docker配置数据' },
        { id: 'image_verify', name: '镜像版本校验', status: 'pending', detail: '逐一检查每个服务的本地镜像状态' },
        { id: 'container_stop', name: '容器停止和清理', status: 'pending', detail: '停止不匹配的容器并清理旧镜像' },
        { id: 'config_fetch', name: 'Gitee配置拉取', status: 'pending', detail: '从Gitee仓库获取最新配置文件' },
        { id: 'container_rebuild', name: '容器服务重建', status: 'pending', detail: '基于Gitee配置拉取镜像并启动服务' },
      ],
      activeStepIndex: 0,
      socket: null,
      showNotification: false,
      notificationTitle: '',
      notificationMessage: '',
      notificationType: 'info', // 'info', 'success', 'warning', 'error'
      executingSteps: new Set(), // 正在执行的步骤
      manualMode: localStorage.getItem('imageUpdateMode') !== 'auto', // 从localStorage读取模式，默认手动
      stepResults: {}, // 每个步骤的执行结果
      // 新增日志相关数据
      executionLogs: [], // 执行日志
      intermediateResults: {}, // 中间结果
      showLogs: true, // 是否显示日志区域
      logsExpanded: false // 日志区域是否展开
    };
  },
  computed: {
    isOperating() {
      return this.status.checking || this.status.updating;
    },
    hasUpdates() {
      return this.status.available_updates && this.status.available_updates.length > 0;
    },
    overallStatusClass() {
      if (this.status.error) return 'status-error';
      if (this.isOperating) return 'status-checking';
      if (this.hasUpdates) return 'status-updates';
      return 'status-ready';
    },
    overallStatusText() {
      if (this.status.error) return '检测失败';
      if (this.isOperating) return '检测中...';
      if (this.hasUpdates) return '发现更新';
      return '检测就绪';
    },
    currentStepText() {
      const runningStep = this.steps.find(s => s.status === 'running');
      if (runningStep) return runningStep.name;
      if (this.isOperating) return '处理中';
      return '就绪';
    }
  },
  mounted() {
    console.log('🚀 [ImageUpdateStatus] 组件挂载开始');
    console.log('📊 [ImageUpdateStatus] 初始化状态:', JSON.stringify(this.status, null, 2));
    console.log('📋 [ImageUpdateStatus] 初始化步骤:', JSON.stringify(this.steps, null, 2));
    console.log('🎛️ [ImageUpdateStatus] 手动模式状态:', this.manualMode);
    
    this.initSocket();
    this.loadStatus();
    this.loadModeConfiguration();
    
    console.log('✅ [ImageUpdateStatus] 组件挂载完成');
  },
  beforeUnmount() {
    if (this.socket) {
      this.socket.disconnect();
    }
  },
  methods: {
    // New UI methods
    getStepClass(status) {
      switch (status) {
        case 'ok': return 'step-completed';
        case 'fail': return 'step-failed';
        case 'running': return 'step-running';
        default: return 'step-pending';
      }
    },
    getStepIcon(status) {
      switch (status) {
        case 'ok': return 'fas fa-check-circle';
        case 'fail': return 'fas fa-times-circle';
        case 'running': return 'fas fa-sync fa-spin';
        default: return 'far fa-circle';
      }
    },
    getStatusBadgeClass(status) {
      switch (status) {
        case 'ok': return 'badge-success';
        case 'fail': return 'badge-error';
        case 'running': return 'badge-processing';
        default: return 'badge-waiting';
      }
    },
    getStatusText(status) {
      switch (status) {
        case 'ok': return '完成';
        case 'fail': return '失败';
        case 'running': return '进行中';
        default: return '等待';
      }
    },
    // Legacy methods for compatibility
    stepClass(status) {
      return this.getStepClass(status);
    },
    stepIcon(status) {
      return this.getStepIcon(status);
    },
    setStep(id, status, detail = '') {
      const step = this.steps.find(s => s.id === id);
      if (!step) {
        console.warn(`⚠️ [STEP-STATUS] 未找到步骤: ${id}`);
        return;
      }
      
      console.log(`📝 [STEP-STATUS] 更新步骤状态: ${id} (${step.name}) -> ${status}`);
      if (detail) {
        console.log(`📝 [STEP-STATUS] 详细信息: ${detail}`);
      }
      
      step.status = status;
      if (detail) step.detail = detail;
      
      console.log(`📝 [STEP-STATUS] 步骤状态已更新:`, JSON.stringify({
        id: step.id,
        name: step.name,
        status: step.status,
        detail: step.detail
      }, null, 2));
      
      // 更新激活步骤索引（第一个非pending视为当前激活）
      const firstRunning = this.steps.findIndex(s => s.status === 'running');
      const firstPending = this.steps.findIndex(s => s.status === 'pending');
      const lastDoneIdx = Math.max(
        this.steps.findIndex((s, i) => s.status === 'ok' && i),
        0
      );
      if (firstRunning >= 0) this.activeStepIndex = firstRunning;
      else if (firstPending >= 0) this.activeStepIndex = Math.max(firstPending - 1, 0);
      else this.activeStepIndex = lastDoneIdx;
    },
    resetSteps() {
      this.steps.forEach(s => { s.status = 'pending'; s.detail = ''; });
    },
    mapProgressToSteps(message) {
      const msg = String(message || '');
      if (!msg) return;
      
      // 步骤1: LWX平台数据解析
      if (msg.includes('步骤1/') && (msg.includes('LWX平台') || msg.includes('数据解析') || msg.includes('设备信息收集与上报'))) {
        this.setStep('lwx_parse', 'running', '解析LWX平台数据...');
      }
      else if (msg.includes('设备信息收集和上报成功') || msg.includes('设备信息上报成功') || msg.includes('LWX平台数据解析完成')) {
        this.setStep('lwx_parse', 'ok', 'LWX平台数据解析完成');
      }
      
      // 步骤2: 镜像版本校验
      else if (msg.includes('步骤2/') && (msg.includes('镜像版本校验') || msg.includes('获取最新配置信息'))) {
        this.setStep('image_verify', 'running', '校验镜像版本...');
      }
      else if (msg.includes('配置获取成功') || msg.includes('找到') && msg.includes('个启用服务') || msg.includes('镜像校验完成')) {
        this.setStep('image_verify', 'ok', '镜像版本校验完成');
      }
      
      // 步骤3: 容器停止和清理
      else if (msg.includes('步骤3/') && (msg.includes('容器停止') || msg.includes('清理') || msg.includes('镜像版本校验'))) {
        this.setStep('container_stop', 'running', '停止和清理容器...');
      }
      else if (msg.includes('容器停止完成') || msg.includes('清理完成')) {
        this.setStep('container_stop', 'ok', '容器停止和清理完成');
      }
      
      // 步骤4: Gitee配置拉取
      else if (msg.includes('步骤4/') && (msg.includes('Gitee') || msg.includes('配置拉取') || msg.includes('容器状态校验'))) {
        this.setStep('config_fetch', 'running', '拉取Gitee配置...');
      }
      else if (msg.includes('容器状态校验完成') || msg.includes('Gitee配置拉取完成') || msg.includes('配置文件拉取成功')) {
        this.setStep('config_fetch', 'ok', 'Gitee配置拉取完成');
      }
      
      // 步骤5: 容器服务重建
      else if (msg.includes('步骤5/') && (msg.includes('容器服务重建') || msg.includes('执行更新操作'))) {
        this.setStep('container_rebuild', 'running', '重建容器服务...');
      }
      else if (msg.includes('更新操作完成') || msg.includes('系统已是最新状态') || msg.includes('容器服务重建完成')) {
        this.setStep('container_rebuild', 'ok', '容器服务重建完成');
      }
      
      // 处理具体进度消息
      if (msg.includes('收集设备信息') || msg.includes('上报设备信息')) {
        this.setStep('lwx_parse', 'running', '处理LWX平台数据...');
      } else if (msg.includes('从LWX平台获取配置') || msg.includes('检查镜像更新')) {
        this.setStep('image_verify', 'running', '校验镜像版本...');
      } else if (msg.includes('停止容器') || msg.includes('清理镜像')) {
        this.setStep('container_stop', 'running', '停止和清理容器...');
      } else if (msg.includes('拉取配置') || msg.includes('Gitee')) {
        this.setStep('config_fetch', 'running', '拉取配置文件...');
      } else if (msg.includes('拉取镜像') || msg.includes('重启容器') || msg.includes('启动服务')) {
        this.setStep('container_rebuild', 'running', '重建容器服务...');
      }
      
      // 错误处理
      if (msg.includes('失败') || msg.includes('异常') || msg.includes('错误')) {
        if (msg.includes('设备信息') || msg.includes('收集') || msg.includes('LWX')) {
          this.setStep('lwx_parse', 'fail', msg);
        } else if (msg.includes('镜像') && (msg.includes('校验') || msg.includes('版本'))) {
          this.setStep('image_verify', 'fail', msg);
        } else if (msg.includes('停止') || msg.includes('清理')) {
          this.setStep('container_stop', 'fail', msg);
        } else if (msg.includes('配置') || msg.includes('Gitee')) {
          this.setStep('config_fetch', 'fail', msg);
        } else if (msg.includes('重建') || msg.includes('启动') || msg.includes('拉取镜像')) {
          this.setStep('container_rebuild', 'fail', msg);
        }
      }
    },
    
    resetPendingSteps() {
      // 只重置还未开始的步骤为pending，保持已完成和正在进行的状态
      this.steps.forEach(step => {
        if (step.status !== 'ok' && step.status !== 'running' && step.status !== 'fail') {
          step.status = 'pending';
          step.detail = '';
        }
      });
    },
    stepElStatus(s) {
      if (s === 'ok') return 'success';
      if (s === 'fail') return 'error';
      if (s === 'running') return 'process';
      return 'wait';
    },
    initSocket() {
      console.log('🔌 [WebSocket] 初始化Socket连接...');
      
      // 连接WebSocket
      this.socket = io('/updates', {
        transports: ['websocket', 'polling']
      });
      
      this.socket.on('connect', () => {
        console.log('✅ [WebSocket] 连接成功');
        console.log('🆔 [WebSocket] Socket ID:', this.socket.id);
      });

      this.socket.on('disconnect', () => {
        console.log('❌ [WebSocket] 连接断开');
      });
      
      // 监听更新状态
      this.socket.on('update_status', (data) => {
        console.log('📡 [WebSocket] 收到状态更新:', JSON.stringify(data, null, 2));
        this.status = { ...this.status, ...data };
        console.log('📊 [WebSocket] 更新后的状态:', JSON.stringify(this.status, null, 2));
      });
      
      // 监听检查进度
      this.socket.on('update_progress', (data) => {
        console.log('📈 [WebSocket] 收到进度更新:', JSON.stringify(data, null, 2));
        this.status.progress = data.progress;
        this.status.message = data.message;
        this.mapProgressToSteps(data.message);
        console.log('📊 [WebSocket] 当前进度:', data.progress + '%');
      });
      
      // 监听检查完成
      this.socket.on('check_complete', (data) => {
        console.log('🏁 [WebSocket] 检查完成:', JSON.stringify(data, null, 2));
        // 将结果映射到步骤“完成”
        if (data && data.error) {
          if (String(data.error).includes('Docker')) this.setStep('docker', 'fail', data.error);
          this.setStep('done', 'fail', data.error);
        } else {
          this.setStep('done', 'ok');
        }
        if (data.available_updates && data.available_updates.length > 0) {
          this.showUpdateNotification(
            '发现更新',
            `发现 ${data.available_updates.length} 个镜像更新`,
            'warning'
          );
        } else if (!data.error) {
          this.showUpdateNotification(
            '检查完成',
            '所有镜像都是最新版本',
            'success'
          );
        }
        
        if (data.error) {
          // 检查是否是Gitee API限制错误
          if (data.error.includes('403') && data.error.includes('gitee.com')) {
            this.showUpdateNotification(
              'API访问受限',
              'Gitee API访问频率受限，请稍后重试（约1-2分钟后）',
              'warning'
            );
          } else {
            this.showUpdateNotification(
              '检查失败',
              data.error,
              'error'
            );
          }
        }
      });
      
      // 监听更新开始
      this.socket.on('update_start', (data) => {
        this.showUpdateNotification(
          '开始更新',
          `正在更新镜像: ${data.name}`,
          'info'
        );
      });
      
      // 监听下载进度
      this.socket.on('download_progress', (data) => {
        this.status.progress = data.progress;
        this.status.message = data.message;
      });
      
      // 监听下载进度
      this.socket.on('download_progress', (data) => {
        this.status.progress = data.progress;
        this.status.message = data.message;
        this.mapProgressToSteps(data.message);
        this.status.updating = data.progress < 100;
      });
      
      // 监听更新完成
      this.socket.on('update_complete', (data) => {
        if (data.success) {
          this.showUpdateNotification(
            '更新完成',
            '镜像更新成功，容器已重启',
            'success'
          );
        } else {
          this.showUpdateNotification(
            '更新失败',
            data.error || '更新过程中发生错误',
            'error'
          );
        }
      });
      
      // 新增: 监听执行日志
      this.socket.on('execution_log', (logData) => {
        this.handleExecutionLog(logData);
      });
      
      // 新增: 监听中间结果
      this.socket.on('intermediate_result', (resultData) => {
        this.handleIntermediateResult(resultData);
      });
      
      // 新增: 监听日志清空
      this.socket.on('logs_cleared', () => {
        this.clearLogs();
      });
    },
    
    async loadStatus() {
      console.log('📥 [API] 开始加载状态...');
      
      try {
        const response = await api.get('/api/image-updates/status');
        console.log('📡 [API] 状态加载响应:', JSON.stringify(response.data, null, 2));
        
        if (response.data.code === 0) {
          console.log('✅ [API] 状态加载成功');
          this.status = { ...this.status, ...response.data.data };
          console.log('📊 [API] 更新后的状态:', JSON.stringify(this.status, null, 2));
        } else {
          console.warn('⚠️ [API] 状态加载响应异常:', response.data);
        }
      } catch (error) {
        console.error('❌ [API] 加载更新状态失败:', error);
        console.error('❌ [API] 错误详情:', error.response?.data || error.message);
      }
    },
    
    async checkUpdates() {
      try {
        this.resetSteps();
        // 调用新的智能更新服务API
        const response = await api.post('/api/intelligent-updates/check', {
          force: true  // 强制检查，清空缓存
        });
        
        if (response.data.code === 0) {
          this.showUpdateNotification(
            '智能检查已开始',
            '正在执行5步骤智能更新流程...',
            'info'
          );
        } else {
          this.showUpdateNotification(
            '检查失败',
            response.data.message || '启动智能更新检查失败',
            'error'
          );
        }
      } catch (error) {
        console.error('触发智能更新检查失败:', error);
        this.showUpdateNotification(
          '检查失败',
          error.response?.data?.message || '无法启动智能更新检查',
          'error'
        );
      }
    },
    
    async loadModeConfiguration() {
      try {
        // 从服务器加载模式配置
        const response = await api.get('/api/intelligent-updates/mode');
        if (response.data && response.data.code === 0) {
          const serverMode = response.data.data.mode;
          this.manualMode = serverMode !== 'auto';
          console.log(`🔧 [CONFIG] 从服务器加载模式配置: ${serverMode}`);
          
          // 更新localStorage
          localStorage.setItem('imageUpdateMode', serverMode);
        }
      } catch (error) {
        console.warn('⚠️ [CONFIG] 从服务器加载模式配置失败，使用默认配置:', error);
        // 如果服务器加载失败，使用localStorage或默认值
        const localMode = localStorage.getItem('imageUpdateMode') || 'manual';
        this.manualMode = localMode !== 'auto';
      }
      
      // 根据最终确定的模式决定是否执行开机检查
      if (!this.manualMode) {
        console.log('🚀 [CONFIG] 自动模式已启用，执行开机检查');
        this.startupCheck();
      } else {
        console.log('🎛️ [CONFIG] 手动模式已启用，跳过开机检查');
      }
    },
    
    async startupCheck() {
      try {
        // 调用新的智能更新服务启动检查
        await api.post('/api/intelligent-updates/startup-check');
      } catch (error) {
        console.error('开机智能更新检查失败:', error);
      }
    },
    
    showUpdateNotification(title, message, type = 'info') {
      this.notificationTitle = title;
      this.notificationMessage = message;
      this.notificationType = type;
      this.showNotification = true;
      
      // 3秒后自动关闭（除非是重要信息）
      if (type !== 'error' && type !== 'warning') {
        setTimeout(() => {
          this.closeNotification();
        }, 3000);
      }
    },
    
    closeNotification() {
      this.showNotification = false;
    },
    
    formatTime(timeString) {
      if (!timeString) return '';
      const date = new Date(timeString);
      return date.toLocaleString('zh-CN');
    },

    // 单步执行相关方法
    canExecuteStep(step, index) {
      // 第一个步骤总是可以执行，后续步骤需要前一个步骤完成
      if (index === 0) return step.status === 'pending' || step.status === 'fail';
      
      // 检查前一个步骤是否完成
      const previousStep = this.steps[index - 1];
      const canExecute = (previousStep.status === 'ok' || previousStep.status === 'skipped') && 
                        (step.status === 'pending' || step.status === 'fail');
      
      return canExecute;
    },

    canRetryStep(step) {
      return step.status === 'fail';
    },

    canSkipStep(step, index) {
      // 最后一步不能跳过，其他失败的步骤可以跳过
      return index < this.steps.length - 1 && step.status === 'fail';
    },

    isStepExecuting(stepId) {
      return this.executingSteps.has(stepId);
    },

    async executeStep(stepId, index) {
      const stepName = this.steps.find(s => s.id === stepId)?.name || stepId;
      console.log(`🚀 [STEP] 开始执行步骤: ${stepId} (${stepName})`);
      
      if (this.isStepExecuting(stepId)) {
        console.warn(`⚠️ [STEP] 步骤 ${stepId} 已在执行中，跳过`);
        return;
      }

      console.log(`🏃 [STEP] 标记步骤 ${stepId} 为执行中...`);
      this.executingSteps.add(stepId);
      this.setStep(stepId, 'running', '执行中...');

      try {
        let result;
        console.log(`🔄 [STEP] 根据步骤ID执行对应操作: ${stepId}`);
        
        switch (stepId) {
          case 'lwx_parse':
            console.log('📊 [STEP] 执行LWX平台数据解析...');
            result = await this.executeLwxParseStep();
            break;
          case 'image_verify':
            console.log('🔍 [STEP] 执行镜像版本校验...');
            result = await this.executeImageVerifyStep();
            break;
          case 'container_stop':
            console.log('🛑 [STEP] 执行容器停止和清理...');
            result = await this.executeContainerStopStep();
            break;
          case 'config_fetch':
            console.log('📥 [STEP] 执行Gitee配置拉取...');
            result = await this.executeConfigFetchStep();
            break;
          case 'container_rebuild':
            console.log('🔨 [STEP] 执行容器服务重建...');
            result = await this.executeContainerRebuildStep();
            break;
          default:
            throw new Error(`未知步骤: ${stepId}`);
        }

        console.log(`📡 [STEP] 步骤 ${stepId} 执行结果:`, JSON.stringify(result, null, 2));

        if (result.success) {
          console.log(`✅ [STEP] 步骤 ${stepId} 执行成功`);
          this.setStep(stepId, 'ok', result.message || '执行成功');
          this.stepResults[stepId] = result.data;
        } else {
          console.error(`❌ [STEP] 步骤 ${stepId} 执行失败:`, result.error);
          this.setStep(stepId, 'fail', result.error || '执行失败');
        }
      } catch (error) {
        console.error(`❌ [STEP] 步骤 ${stepId} 执行异常:`, error);
        console.error(`❌ [STEP] 错误详情:`, error.response?.data || error.message);
        this.setStep(stepId, 'fail', error.message || '执行异常');
      } finally {
        console.log(`🏁 [STEP] 步骤 ${stepId} 执行结束，清理执行状态`);
        this.executingSteps.delete(stepId);
      }
    },

    async retryStep(stepId, index) {
      // 重置步骤状态并重新执行
      this.setStep(stepId, 'pending', '准备重试...');
      await this.$nextTick();
      await this.executeStep(stepId, index);
    },

    skipStep(stepId, index) {
      this.setStep(stepId, 'skipped', '已跳过');
      this.stepResults[stepId] = { skipped: true };
    },

    // 各步骤的具体执行方法
    async executeLwxParseStep() {
      try {
        console.log('📊 [LWX-PARSE] 开始LWX平台数据解析...');
        
        // 调用后端API执行真实的LWX平台数据解析
        const schoolName = await this.getCurrentSchoolName();
        console.log('🏫 [LWX-PARSE] 学校名称:', schoolName);
        
        const response = await api.post('/api/intelligent-updates/lwx-parse', {
          school_name: schoolName
        });
        
        console.log('📡 [LWX-PARSE] API响应:', response.data);
        
        if (response.data.code === 0) {
          const data = response.data.data;
          console.log('✅ [LWX-PARSE] LWX平台数据解析成功');
          console.log('📊 [LWX-PARSE] 解析结果:', data);
          
          return {
            success: true,
            message: `LWX平台数据解析完成 - ${data.summary || '数据已解析'}`,
            data: data
          };
        } else {
          console.error('❌ [LWX-PARSE] LWX平台数据解析失败:', response.data.message);
          return {
            success: false,
            error: response.data.message || 'LWX平台数据解析失败'
          };
        }
      } catch (error) {
        console.error('❌ [LWX-PARSE] LWX平台数据解析请求失败:', error);
        return {
          success: false,
          error: 'LWX平台数据解析请求失败: ' + error.message
        };
      }
    },

    async getCurrentSchoolName() {
      try {
        // 尝试从后端获取当前绑定的学校名称
        const response = await api.get('/api/device/current-school');
        if (response.data.code === 0 && response.data.data?.school_name) {
          return response.data.data.school_name;
        }
      } catch (error) {
        console.warn('获取当前学校名称失败:', error);
      }
      // 如果获取失败，使用默认值
      return '测试学校';
    },

    async executeImageVerifyStep() {
      try {
        const schoolName = await this.getCurrentSchoolName();
        const response = await api.post('/api/intelligent-updates/verify-images', {
          school_name: schoolName
        });
        if (response.data.code === 0) {
          const needsUpdateCount = response.data.data.services_need_action ? response.data.data.services_need_action.length : 0;
          const totalServices = response.data.data.verification_results ? response.data.data.verification_results.length : 0;
          
          return {
            success: true,
            message: `镜像校验完成，发现 ${needsUpdateCount} 个需要处理的服务（共${totalServices}个服务）`,
            data: response.data.data
          };
        } else {
          return {
            success: false,
            error: response.data.message || '镜像校验失败'
          };
        }
      } catch (error) {
        return {
          success: false,
          error: '镜像校验请求失败: ' + error.message
        };
      }
    },

    async executeContainerStopStep() {
      try {
        const schoolName = await this.getCurrentSchoolName();
        const response = await api.post('/api/intelligent-updates/stop-containers', {
          school_name: schoolName
        });
        if (response.data.code === 0) {
          return {
            success: true,
            message: `容器停止完成，处理了 ${response.data.data.stopped_count || 0} 个容器`,
            data: response.data.data
          };
        } else {
          return {
            success: false,
            error: response.data.message || '容器停止失败'
          };
        }
      } catch (error) {
        return {
          success: false,
          error: '容器停止请求失败: ' + error.message
        };
      }
    },

    async executeConfigFetchStep() {
      console.log('📥 [CONFIG-FETCH] 开始执行Gitee配置拉取步骤...');
      
      try {
        console.log('📡 [CONFIG-FETCH] 发送API请求: /api/intelligent-updates/fetch-config');
        const schoolName = await this.getCurrentSchoolName();
        const response = await api.post('/api/intelligent-updates/fetch-config', {
          school_name: schoolName
        });
        
        console.log('📡 [CONFIG-FETCH] API响应:', JSON.stringify(response.data, null, 2));
        
        if (response.data.code === 0) {
          console.log('✅ [CONFIG-FETCH] 配置拉取成功');
          if (response.data.data?.config_path) {
            console.log('📁 [CONFIG-FETCH] 配置文件路径:', response.data.data.config_path);
          }
          return {
            success: true,
            message: 'Gitee配置拉取完成',
            data: response.data.data
          };
        } else {
          console.error('❌ [CONFIG-FETCH] 配置拉取失败:', response.data.message);
          return {
            success: false,
            error: response.data.message || 'Gitee配置拉取失败'
          };
        }
      } catch (error) {
        console.error('❌ [CONFIG-FETCH] 网络请求失败:', error);
        console.error('❌ [CONFIG-FETCH] 错误详情:', error.response?.data || error.message);
        return {
          success: false,
          error: 'Gitee配置拉取请求失败: ' + error.message
        };
      }
    },

    async executeContainerRebuildStep() {
      try {
        const schoolName = await this.getCurrentSchoolName();
        // 使用长超时版本的POST请求，因为容器重建可能需要较长时间
        const response = await api.postWithLongTimeout('/api/intelligent-updates/rebuild-containers', {
          school_name: schoolName
        }, 180000); // 3分钟超时
        if (response.data.code === 0) {
          return {
            success: true,
            message: `容器重建完成，启动了 ${response.data.data.started_count || 0} 个服务`,
            data: response.data.data
          };
        } else {
          return {
            success: false,
            error: response.data.message || '容器重建失败'
          };
        }
      } catch (error) {
        return {
          success: false,
          error: '容器重建请求失败: ' + error.message
        };
      }
    },

    // 切换手动/自动模式
    async toggleManualMode() {
      this.manualMode = !this.manualMode;
      const mode = this.manualMode ? 'manual' : 'auto';
      console.log(`🔄 [MODE] 切换到${this.manualMode ? '手动' : '自动'}模式`);
      
      try {
        // 保存模式设置到服务器
        await api.post('/api/intelligent-updates/mode', { mode: mode });
        
        // 也保存到localStorage作为备份
        localStorage.setItem('imageUpdateMode', mode);
        
        if (this.manualMode) {
          this.showUpdateNotification(
            '手动模式',
            '已切换到手动模式，可以单独执行每个步骤',
            'info'
          );
        } else {
          this.showUpdateNotification(
            '自动模式',
            '已切换到自动模式，将执行完整流程',
            'info'
          );
          // 切换到自动模式时立即执行检查
          setTimeout(() => {
            this.checkUpdates();
          }, 500);
        }
      } catch (error) {
        console.error('❌ 保存模式设置失败:', error);
        // 如果保存失败，回滚状态
        this.manualMode = !this.manualMode;
        this.showUpdateNotification(
          '设置失败',
          '模式切换失败，请稍后重试',
          'error'
        );
      }
    },

    // 重置所有步骤
    resetAllSteps() {
      this.steps.forEach(step => {
        step.status = 'pending';
        step.detail = step.detail.split(' - ')[0]; // 保留原始描述
      });
      this.executingSteps.clear();
      this.stepResults = {};
      this.status.progress = 0;
      this.status.message = '已重置';
    },

    // 日志相关方法
    clearLogs() {
      this.executionLogs = [];
      this.intermediateResults = {};
      console.log('🗑️ [日志] 已清空执行日志和中间结果');
    },
    
    toggleLogsExpand() {
      this.logsExpanded = !this.logsExpanded;
      console.log(`📋 [日志] 日志区域${this.logsExpanded ? '展开' : '收起'}`);
    },
    
    getLogIcon(type) {
      const icons = {
        'info': 'fas fa-info-circle',
        'success': 'fas fa-check-circle',
        'warning': 'fas fa-exclamation-triangle',
        'error': 'fas fa-times-circle'
      };
      return icons[type] || 'fas fa-info-circle';
    },
    
    getStepDisplayName(step) {
      const stepNames = {
        'config_fetch': '配置获取',
        'image_verify': '镜像校验',
        'container_stop': '容器停止',
        'container_rebuild': '容器重建',
        'lwx_parse': 'LWX解析'
      };
      return stepNames[step] || step;
    },
    
    // 处理来自后端的日志消息
    handleExecutionLog(logData) {
      this.executionLogs.push(logData);
      console.log(`📄 [日志] 收到执行日志: ${logData.type} - ${logData.message}`);
      
      // 保持日志数量在可管理范围内
      if (this.executionLogs.length > 100) {
        this.executionLogs = this.executionLogs.slice(-50);
      }
    },
    
    // 处理来自后端的中间结果
    handleIntermediateResult(resultData) {
      this.intermediateResults[resultData.step] = resultData.result;
      console.log(`📊 [结果] 收到中间结果: ${resultData.step}`);
    }
  }
};
</script>

<style scoped>
/* Main container */
.image-update-status {
  margin: 20px 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
}

/* Progress section */
.main-progress-section {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 16px;
  padding: 30px;
  color: white;
  margin-bottom: 20px;
  box-shadow: 0 8px 32px rgba(102, 126, 234, 0.3);
}

.progress-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
}

.progress-header h2 {
  margin: 0;
  font-size: 24px;
  font-weight: 700;
}

.overall-status {
  padding: 8px 16px;
  border-radius: 20px;
  font-size: 14px;
  font-weight: 600;
  background: rgba(255, 255, 255, 0.2);
  backdrop-filter: blur(10px);
}

.status-ready { background: rgba(76, 175, 80, 0.3); }
.status-checking { background: rgba(33, 150, 243, 0.3); }
.status-updates { background: rgba(255, 152, 0, 0.3); }
.status-error { background: rgba(244, 67, 54, 0.3); }

/* Circular progress */
.circular-progress {
  display: flex;
  justify-content: center;
  margin: 20px 0;
}

.progress-circle {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}

.progress-circle.spinning {
  animation: pulse 2s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.05); }
}

.progress-fill {
  transition: stroke-dashoffset 0.5s ease;
  filter: drop-shadow(0 0 8px rgba(59, 130, 246, 0.8));
}

.progress-text {
  position: absolute;
  text-align: center;
}

.progress-number {
  font-size: 28px;
  font-weight: 700;
  line-height: 1;
}

.progress-status {
  font-size: 12px;
  opacity: 0.9;
  margin-top: 4px;
}

.status-message {
  text-align: center;
  font-size: 16px;
  opacity: 0.9;
  background: rgba(255, 255, 255, 0.1);
  padding: 12px;
  border-radius: 8px;
  backdrop-filter: blur(10px);
}

/* Steps section */
.steps-section {
  background: white;
  border-radius: 16px;
  padding: 24px;
  margin-bottom: 20px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
  border: 1px solid #f0f0f0;
}

.steps-section h3 {
  margin: 0 0 20px 0;
  font-size: 20px;
  font-weight: 700;
  color: #2c3e50;
}

.steps-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.step-item {
  display: flex;
  align-items: flex-start;
  padding: 16px;
  border-radius: 12px;
  border: 2px solid transparent;
  transition: all 0.3s ease;
  background: #f8f9fa;
}

.step-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  min-width: 160px;
  margin-left: auto;
}

.step-buttons {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

.step-button {
  padding: 4px 8px;
  border: none;
  border-radius: 4px;
  font-size: 11px;
  cursor: pointer;
  transition: all 0.2s ease;
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 50px;
  justify-content: center;
}

.execute-btn {
  background: #3b82f6;
  color: white;
}

.execute-btn:hover {
  background: #2563eb;
}

.execute-btn:disabled {
  background: #9ca3af;
  cursor: not-allowed;
}

.execute-btn.executing {
  background: #f59e0b;
}

.retry-btn {
  background: #f59e0b;
  color: white;
}

.retry-btn:hover {
  background: #d97706;
}

.skip-btn {
  background: #6b7280;
  color: white;
}

.skip-btn:hover {
  background: #4b5563;
}

.mode-controls {
  display: flex;
  gap: 12px;
  margin-bottom: 16px;
  justify-content: center;
}

.mode-toggle-btn {
  padding: 8px 16px;
  border: 2px solid #e5e7eb;
  border-radius: 8px;
  background: white;
  color: #374151;
  cursor: pointer;
  transition: all 0.2s ease;
  display: flex;
  align-items: center;
  gap: 8px;
}

.mode-toggle-btn:hover {
  border-color: #3b82f6;
  color: #3b82f6;
}

.mode-toggle-btn.manual-active {
  background: #3b82f6;
  color: white;
  border-color: #3b82f6;
}

.reset-btn {
  padding: 8px 16px;
  border: 2px solid #f59e0b;
  border-radius: 8px;
  background: white;
  color: #f59e0b;
  cursor: pointer;
  transition: all 0.2s ease;
  display: flex;
  align-items: center;
  gap: 8px;
}

.reset-btn:hover {
  background: #f59e0b;
  color: white;
}

.reset-btn:disabled {
  border-color: #d1d5db;
  color: #9ca3af;
  cursor: not-allowed;
}

.manual-mode-info {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 16px;
  background: #e0f2fe;
  border: 1px solid #0284c7;
  border-radius: 8px;
  color: #0c4a6e;
  font-size: 14px;
}

.step-pending {
  border-color: #e9ecef;
  background: #f8f9fa;
}

.step-running {
  border-color: #3b82f6;
  background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%);
  box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2);
}

.step-completed {
  border-color: #10b981;
  background: linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%);
}

.step-failed {
  border-color: #ef4444;
  background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%);
}

.step-icon {
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  margin-right: 16px;
  font-size: 18px;
}

.step-pending .step-icon { background: #e9ecef; color: #6c757d; }
.step-running .step-icon { background: #3b82f6; color: white; }
.step-completed .step-icon { background: #10b981; color: white; }
.step-failed .step-icon { background: #ef4444; color: white; }

.step-content {
  flex: 1;
}

.step-title {
  font-size: 16px;
  font-weight: 600;
  color: #2c3e50;
  margin-bottom: 4px;
}

.step-detail {
  font-size: 14px;
  color: #6b7280;
}

.step-status {
  margin-left: 16px;
}

.status-badge {
  padding: 6px 12px;
  border-radius: 16px;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.badge-waiting {
  background: #f3f4f6;
  color: #6b7280;
}

.badge-processing {
  background: #dbeafe;
  color: #1d4ed8;
}

.badge-success {
  background: #d1fae5;
  color: #065f46;
}

.badge-error {
  background: #fee2e2;
  color: #991b1b;
}

/* Action section */
.action-section {
  text-align: center;
  margin-bottom: 20px;
}

.check-button {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  border: none;
  padding: 16px 32px;
  border-radius: 12px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s ease;
  box-shadow: 0 4px 16px rgba(102, 126, 234, 0.3);
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.check-button:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}

.check-button:disabled {
  opacity: 0.7;
  cursor: not-allowed;
  transform: none;
}

.check-button.checking {
  background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
}

/* Updates section */
.updates-section {
  background: white;
  border-radius: 16px;
  padding: 24px;
  margin-bottom: 20px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
  border: 1px solid #f0f0f0;
}

.updates-section h3 {
  margin: 0 0 16px 0;
  font-size: 18px;
  font-weight: 700;
  color: #f59e0b;
}

.update-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.update-item {
  padding: 16px;
  background: #fef3c7;
  border-radius: 12px;
  border-left: 4px solid #f59e0b;
}

.update-info {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}

.update-info strong {
  color: #92400e;
  font-weight: 600;
}

.update-reason {
  color: #d97706;
  font-size: 12px;
  background: #fbbf24;
  padding: 4px 8px;
  border-radius: 8px;
}

.update-image {
  font-size: 12px;
  color: #78716c;
  font-family: 'Monaco', 'Consolas', monospace;
  background: #f5f5f4;
  padding: 8px;
  border-radius: 6px;
  word-break: break-all;
}

/* Error section */
.error-section {
  background: #fee2e2;
  border: 1px solid #fecaca;
  border-radius: 12px;
  padding: 16px;
  margin-bottom: 20px;
  color: #991b1b;
  display: flex;
  align-items: center;
  gap: 12px;
  font-weight: 500;
}

.error-section i {
  font-size: 20px;
  color: #dc2626;
}

/* Last check */
.last-check {
  text-align: center;
  font-size: 12px;
  color: #9ca3af;
  font-style: italic;
}

/* 执行日志区域样式 */
.execution-logs {
  margin: 20px 0;
}

.logs-card {
  border-radius: 12px;
  overflow: hidden;
}

.logs-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.logs-header h4 {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  color: #374151;
}

.logs-controls {
  display: flex;
  gap: 8px;
}

.logs-content {
  max-height: 400px;
  overflow-y: auto;
  transition: max-height 0.3s ease;
}

.logs-content.expanded {
  max-height: 800px;
}

.log-section, .result-section {
  margin-bottom: 20px;
}

.log-section h5, .result-section h5 {
  margin: 0 0 12px 0;
  padding: 8px 12px;
  background: #f3f4f6;
  border-radius: 6px;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: #374151;
}

.log-entries {
  max-height: 300px;
  overflow-y: auto;
  border: 1px solid #e5e7eb;
  border-radius: 6px;
  background: #fafafa;
}

.log-entry {
  padding: 8px 12px;
  border-bottom: 1px solid #e5e7eb;
  font-family: 'Monaco', 'Consolas', monospace;
  font-size: 12px;
  display: flex;
  align-items: flex-start;
  gap: 8px;
}

.log-entry:last-child {
  border-bottom: none;
}

.log-entry.log-info {
  background: #eff6ff;
  border-left: 3px solid #3b82f6;
}

.log-entry.log-success {
  background: #f0fdf4;
  border-left: 3px solid #22c55e;
}

.log-entry.log-warning {
  background: #fffbeb;
  border-left: 3px solid #f59e0b;
}

.log-entry.log-error {
  background: #fef2f2;
  border-left: 3px solid #ef4444;
}

.log-time {
  color: #6b7280;
  font-size: 10px;
  min-width: 60px;
}

.log-message {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 6px;
}

.log-details {
  margin-top: 8px;
  width: 100%;
}

.log-details pre {
  background: #f9fafb;
  padding: 8px;
  border-radius: 4px;
  font-size: 11px;
  color: #374151;
  overflow-x: auto;
}

.result-entries {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.result-entry {
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  padding: 12px;
  background: #ffffff;
}

.result-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}

.result-step {
  font-weight: 600;
  color: #374151;
}

.result-time {
  font-size: 12px;
  color: #6b7280;
  font-family: 'Monaco', 'Consolas', monospace;
}

.result-content {
  font-size: 13px;
}

.no-logs {
  text-align: center;
  padding: 40px 20px;
  color: #6b7280;
  font-style: italic;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

.no-logs i {
  font-size: 24px;
  opacity: 0.5;
}

/* Legacy styles for compatibility */
.tiles-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
}
.tile {
  display: grid;
  grid-template-columns: 36px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border-radius: 12px;
  box-shadow: 0 6px 18px rgba(0,0,0,0.06);
  background: #fff;
  border: 1px solid #f1f2f6;
}
.tile-icon i { font-size: 22px; }
.tile-title { font-weight: 700; font-size: 15px; color: #2c3e50; }
.tile-detail { font-size: 12px; color: #6b7280; margin-top: 2px; }
.tile-badge { font-size: 12px; padding: 4px 8px; border-radius: 999px; }

/* 颜色语义 */
.tile-pending { border-left: 6px solid #cbd5e1; }
.tile-running { border-left: 6px solid #3b82f6; }
.tile-ok { border-left: 6px solid #10b981; }
.tile-fail { border-left: 6px solid #ef4444; }
.tile-pending .tile-icon i { color: #64748b; }
.tile-running .tile-icon i { color: #2563eb; }
.tile-ok .tile-icon i { color: #059669; }
.tile-fail .tile-icon i { color: #dc2626; }

.badge-pending { background: #e5e7eb; color: #374151; }
.badge-running { background: #dbeafe; color: #1d4ed8; }
.badge-ok { background: #d1fae5; color: #065f46; }
.badge-fail { background: #fee2e2; color: #991b1b; }

.update-notification {
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: 1000;
  min-width: 300px;
  max-width: 400px;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
  animation: slideIn 0.3s ease;
}

@keyframes slideIn {
  from {
    transform: translateX(100%);
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}

.notification-content {
  padding: 15px;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  position: relative;
}

.notification-info {
  background: #e3f2fd;
  border-left: 4px solid #2196f3;
}

.notification-success {
  background: #e8f5e8;
  border-left: 4px solid #4caf50;
}

.notification-warning {
  background: #fff3e0;
  border-left: 4px solid #ff9800;
}

.notification-error {
  background: #ffebee;
  border-left: 4px solid #f44336;
}

.notification-text {
  flex: 1;
}

.notification-text h4 {
  margin: 0 0 5px 0;
  font-size: 14px;
  font-weight: 600;
}

.notification-text p {
  margin: 0;
  font-size: 13px;
  color: #666;
}

.close-btn {
  background: none;
  border: none;
  cursor: pointer;
  color: #999;
  padding: 0;
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.close-btn:hover {
  color: #666;
}
</style>